home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / VideoToolboxSources / PixMapToPostScript.c < prev    next >
Text File  |  1995-07-27  |  12KB  |  279 lines

  1. /*
  2. PixMapToPostScript.c
  3. Copyright © 1991,1992,1993 Denis G. Pelli
  4.  
  5.     PixMapToPostScript(filename,pm,rectPtr,pageRectPtr,cellsPerInch,grayLevels);
  6.  
  7.     This is a simple but reasonably general routine to convert a grayscale
  8. PixMap to a PostScript file that may be transmitted to a LaserWriter or Linotype
  9. to produce a halftone image on paper. We use it a lot; it works well.
  10.     The filename, by convention, should end in ".ps" to indicate that it is
  11. a postscript file, but this is not enforced. The file's type is set to 'TEXT'
  12. with creator '4PSU', so that it can be double-clicked to open my favorite
  13. PostScript downloading program, PostHaste. However, it's a plain text file so
  14. any downloader will work.
  15.     The PixMap must have 8 bits per pixel. No color tables are used. The raw
  16. pixel value (Apple calls it an "index") is sent directly to the printer with no
  17. transformation. PostScript assumes that the number, from 0 to 255 is
  18. proportional to desired reflectance, from zero to 1. The *rectPtr indicates what
  19. part of your PixMap is to be used.
  20.     The *pageRectPtr is subtle. It describes, in typographers points
  21. (1/72"), the rectangle that your image will be mapped onto on the printed page.
  22. It is essential that you keep in mind that Apple and Adobe use different
  23. coordinate systems. Both Apple and Adobe increase x from left to right. However,
  24. Apple has y increasing from top to bottom, whereas Adobe increases y from bottom
  25. to top. Adobe's origin is the lower left corner of the page, even though that
  26. point is usually not printable, since most printers can only print to within
  27. about a half inch of the edge. The pageRect, though supplied in Apple's Rect
  28. data structure, must be in Adobe's coordinates, respecting the names of the Rect
  29. structure's fields: left, top, right, bottom. So, for an image to fill most of
  30. an 8.5x11 page, with 0.5" margins, you might use the following:
  31.  
  32.         SetRect(&pageRect,0.5*72,10.5*72,8*72,0.5*72);
  33.  
  34.     In printing PostScript halftones the halftone cell size determines both
  35. the spatial and graylevel resolutions of the resulting image. For the convenience
  36. of the user this can be specified by setting either the cellsPerInch or the
  37. grayLevels argument to the desired value; the other one should be zero. If both
  38. are zero then the printer will be left at its default cell size, which is
  39. usually a good choice. Note that there need not be any particular correspondence
  40. between pixels in your image and cells in the halftone; the printer
  41. automatically resamples your image to produce the halftone.
  42.     If you set cellsPerInch to a nonzero value then the printer will be
  43. asked to print its halftone with that many halftone cells per inch. E.g. to
  44. produce a halftone original for subsequent one-to-one reproduction in a journal,
  45. you'll want the cells to be coarse enough for them to reproduce without
  46. re-screening, e.g. 100 cells per inch.
  47.     Alternatively, if you set grayLevels to a nonzero value then the printer
  48. will be asked to print its halftone with cells containing grayLevels-1 printer
  49. pixels, yielding the specified number of gray levels. E.g. you might want to
  50. force your 300 dpi LaserWriter to use big cells yielding 256 gray levels.
  51.     You should remove() any pre-existing old file of the same name, unless
  52. you want to append to it. PixMapToPostScript always appends to an existing file
  53. with the same name, if one exists, to allow you to place several images onto a
  54. page. (You should offset their pageRects unless you want the images to
  55. superimpose.)
  56.     You will need to add a "showpage\n" command at the end of the postscript
  57. file. This is the command that tells the printer to go ahead and print
  58. the page. Use the subroutine AppendToFile(filename,"showpage\n"), supplied for this
  59. purpose. Naturally, you could also use to this to add your own postscript
  60. commands before or after the imaging code inserted by PixMapToPostScript.
  61.     Here's a minimal example of the three commands:
  62.  
  63.         remove("test.ps");
  64.         PixMapToPostScript("test.ps",pixMapHandle,&rect,&pageRect,0.0,0);
  65.         AppendToFile("test.ps","showpage\n");
  66.  
  67.     To print a screen to disk, preserving the size and scale of the image,
  68. try this:
  69.  
  70.         remove("test.ps");
  71.         pageRect=window->portRect;
  72.         pageRect.top*=-1;        // convert from Apple to Adobe coordinates
  73.         pageRect.bottom*=-1;    // convert from Apple to Adobe coordinates
  74.         SetRect(&paperRect,0,11*72,8.5*72,0);
  75.         CenterRectInRect(&pageRect,&paperRect);
  76.         PixMapToPostScript("test.ps",window->portPixMap,&window->portRect,&pageRect,0,0);
  77.         AppendToFile("test.ps","showpage\n");
  78.  
  79.     Tiling is something we often want to do, creating a huge image by taping
  80. many pages together. You accomplish this by repeatedly printing the huge
  81. image--only one pageful appears each time--shifting the image so that eventually
  82. every bit has been printed. (It's slow, since the whole image is
  83. transmitted to the printer each time.) Here's an example that creates a big "width"
  84. by "height" image. We tile onto multiple 8.5"x11" pages, using 7.5"x10" of each
  85. page, allowing for 0.5" nonprinting margins:
  86.  
  87.         remove("test.ps");
  88.         SetRect(&mosaicRect,0,height,width,0);    // In units of "points". 72 points/inch.
  89.         OffsetRect(&mosaicRect,0.5*72,0.5*72);    // allow for nonprinting margin
  90.         for(i=0;i<width;i+=7.5*72)for(j=0;j<height;j+=10*72){
  91.             pageRect=mosaicRect;
  92.             OffsetRect(&pageRect,-i,-j);
  93.             PixMapToPostScript("test.ps",pixMapHandle,&rect,&pageRect,0.0,0);
  94.             AppendToFile("test.ps","showpage\n");
  95.         }
  96.  
  97.     In case your PostScript manual isn't handy, the easy way to obtain
  98. multiple copies of each page is to use the #copies variable that's built into
  99. Postscript. Anywhere in your file before "showpage", set #copies to the desired
  100. value. E.g.
  101.  
  102.         AppendToFile("test.ps","/#copies 3 def\n");
  103.         
  104.     There are many free PostScript downloaders, but I find them clumsy to
  105. use, especially the ones that don't inform you of errors, should they occur. I
  106. bought and use
  107.  
  108. BBEdit (comes with an extension for downloading postscript--i don't know if it reports errors)
  109.  
  110. PostHaste (alas, it's not 32-bit clean, and thus is no longer usable)
  111. from:
  112. Micro Dynamics, Ltd.
  113. 8555 16th St., Suite 802
  114. Silver Spring, MD 20910
  115. (301)-589-6300
  116.  
  117. and
  118.  
  119. LaserStatus, a desk accessory included in the 
  120. MockPackage Plus Utilities
  121. from:
  122. CE Software
  123. 1854 Fuller Road
  124. PO Box 65580
  125. West Des Moines, Iowa 50265
  126. (515)-224-1995
  127.  
  128. You may also want to read:
  129.  
  130. Adobe Systems (1985) PostScript Language Reference Manual, Second Edition. 
  131. Reading, MA: Addison-Wesley.
  132.  
  133. Pelli, D. G. (1987) Programming in PostScript: Imaging on paper from a mathematical
  134. description. BYTE, 12 (5), 185-202.
  135.  
  136. HISTORY:
  137. 4/21/91    dgp    wrote it
  138. 7/24/91 dgp added comment about shifting pageRect
  139. 8/24/91    dgp    Made compatible with THINK C 5.0
  140. 12/7/91    dgp    minor editing of comments
  141. 10/10/92 dgp Added support for Pixmap's that require 32-bit addressing.
  142.             Much faster now, using table-lookup instead of sprintf for 
  143.             the hex encoding.
  144.             Deleted obsolete support for THINK C 4.
  145. 4/29/93    dgp    & jas Added explanation of how to do tiling.
  146. 5/27/93    dgp    minor editing for speed and clarity
  147. 6/15/93    dgp    Fixed silly bug introduced 5/27/93 that suppressed all hex data.
  148.             Call StripAddress.
  149. 6/29/93    dgp    Changed call interface to accept a pixmap handle instead of a pointer.
  150.             The problem with accepting a pointer is that the user must remember
  151.             to lock the handle before dereferencing it to get the pointer, and
  152.             lots of people forget, leading to a mysterious crash. Now the locking
  153.             is handled internally, automatically.
  154. 6/30/93    dgp    Added example, above, showing how to print screen to disk.
  155. 7/9/93    dgp check for 32-bit addressing capability.
  156. 12/15/93 dgp added filename argument to the diagnostic messages.
  157.             Corrected grayLevels to grayLevels-1 in computing
  158.             the required number of pixels in the halftone cell.
  159. 6/18/94    dgp    can32 is now computed by calling TrapAvailable(_SwapMMUMode), which 
  160.             returns the correct answer even on Macs with dirty ROMs.
  161. 9/5/94 dgp removed assumption in printf's that int==short.
  162. 5/23/95 dgp Apple changed the prototype in the header file from SwapMMUMode(char *) to 
  163.             SwapMMUMode(signed char *). To retain compatibility with both old and new
  164.             headers, I cast the argument (void *).
  165. */
  166. #include "VideoToolbox.h"
  167. #ifndef __TRAPS__
  168.     #include <Traps.h>        // _SwapMMUMode
  169. #endif
  170. #define CREATOR '4PSU'    /* identifies the postscript file as a PostHaste document */
  171.  
  172. void PixMapToPostScript(char *filename,PixMap **pm,Rect *rectPtr
  173.     ,Rect *pageRectPtr,double cellsPerInch,int grayLevels)
  174. {
  175.     FILE *file;
  176.     unsigned char *addr;
  177.     long y,bytes,width;
  178.     register long i;
  179.     unsigned short *buffer,*buffer32;
  180.     register unsigned short *word,hex[256];
  181.     register unsigned char *byte;
  182.     short pixelSize;
  183.     short rowBytes;
  184.     time_t ANSITime;
  185.     char string[100];
  186.     signed char mode;
  187.     char pmState;
  188.     Boolean can32;
  189.  
  190.     assert(StackSpace()>5000);
  191.     can32=TrapAvailable(_SwapMMUMode);
  192.     if(cellsPerInch!=0.0 && grayLevels!=0.0)PrintfExit("PixMapToPostScript(%s): "
  193.         "you may not specify BOTH cellsPerInch & grayLevels.\n"
  194.         "Set one to zero.\n",filename);
  195.     file=fopen(filename,"a");
  196.     if(file==NULL)PrintfExit("PixMapToPostScript: Error in opening file “%s”.\n"
  197.         ,filename);
  198.     pmState=HGetState((Handle)pm);
  199.     HLock((Handle)pm);
  200.     addr=RectToAddress(*pm,rectPtr,&rowBytes,&pixelSize,NULL);
  201.     if(addr==NULL)PrintfExit("PixMapToPostScript(%s): Bad PixMap.\n",filename);
  202.     if(pixelSize!=8)PrintfExit("PixMapToPostScript(%s): "
  203.         "Sorry, pixelSize must be 8, not %d.\n",filename,(int)pixelSize);
  204.     time(&ANSITime);
  205.     strftime(string,sizeof(string),"%I:%M %p %A, %B %d, %Y",localtime(&ANSITime));
  206.     fprintf(file,
  207.         "\nsave                    %% %s, %s\n",filename,string);
  208.     fprintf(file,
  209.         "/nx %d def                %% pixels per raster line\n",(int)(rectPtr->right-rectPtr->left));
  210.     fprintf(file,
  211.         "/ny %d def                %% lines in image\n",(int)(rectPtr->bottom-rectPtr->top));
  212.     fprintf(file,
  213.         "%d %d translate            %% locate lower left of image\n"
  214.         ,(int)pageRectPtr->left,(int)pageRectPtr->bottom);
  215.     fprintf(file,
  216.         "%d %d scale            %% print image with these dimensions on page\n"
  217.         ,(int)(pageRectPtr->right-pageRectPtr->left),(int)(pageRectPtr->top-pageRectPtr->bottom));
  218.     fprintf(file,
  219.         "/hypotenuse {dup mul exch dup mul add sqrt} bind def\n"
  220.         "/pixelsPerInch gsave initmatrix 72 0 dtransform hypotenuse grestore def\n");
  221.     if(cellsPerInch!=0.0){
  222.         fprintf(file,
  223.             "/cellsPerInch %.2f def    %% halftone dot frequency\n",cellsPerInch);
  224.         fprintf(file,
  225.             "cellsPerInch currentscreen 4 -2 roll pop 3 1 roll setscreen\n");
  226.     }
  227.     if(grayLevels!=0){
  228.         fprintf(file,
  229.             "/cellsPerInch pixelsPerInch %.2f div def    %% halftone dot frequency\n"
  230.             ,sqrt(grayLevels-1));
  231.         fprintf(file,
  232.             "cellsPerInch currentscreen 4 -2 roll pop 3 1 roll setscreen\n");
  233.     }
  234.     fprintf(file,
  235.         "/s nx string def        %% string to hold one raster line\n"
  236.         "nx ny 8                    %% dimensions and bits/pixel of source image\n"
  237.         "[nx 0 0 ny neg 0 ny]    %% map unit square to PixMap data\n"
  238.         "{currentfile s readhexstring pop}    %% read data\n"
  239.         "bind                    %% speed up reading\n"
  240.         "image\n");
  241.     assert(sizeof(*byte)==1);    // required by our algorithm
  242.     assert(sizeof(*word)==2);    // required by our algorithm
  243.     for(i=0;i<256;i++){
  244.         sprintf(string,"%02x",(int)i);
  245.         hex[i]=*(unsigned short *)string;
  246.     }
  247.     width=rectPtr->right-rectPtr->left;
  248.     bytes=(width+1)*sizeof(*word);
  249.     buffer=(unsigned short *)NewPtr(bytes);
  250.     if(buffer==NULL)PrintfExit("PixMapToPostScript(%s): "
  251.         "no room for %ld byte buffer.\n\007",filename,bytes);
  252.     buffer32=(unsigned short *)StripAddress(buffer);
  253.     for(y=rectPtr->top;y<rectPtr->bottom;y++){
  254.         mode=true32b;
  255.         if(can32)SwapMMUMode((void *)&mode);
  256.         byte=addr;
  257.         word=buffer32;
  258.         for(i=0;i<width;i++) *word++ = hex[*byte++];
  259.         *word=0;
  260.         if(can32)SwapMMUMode((void *)&mode);
  261.         fprintf(file,"%s\n",(char *)buffer);
  262.         addr+=rowBytes;
  263.     }
  264.     HSetState((Handle)pm,pmState);
  265.     DisposPtr((Ptr)buffer);
  266.     fprintf(file,"restore\n");
  267.     fclose(file);
  268.     SetFileInfo(filename,'TEXT',CREATOR);
  269. }
  270.  
  271. void AppendToFile(char *filename,char *string)
  272. {
  273.     FILE *file;
  274.     
  275.     file=fopen(filename,"a");
  276.     if(file==NULL)PrintfExit("AppendToFile: Error in opening file \"%s\"\n",filename);
  277.     fprintf(file,"%s",string);
  278.     fclose(file);
  279. }